/**
 *   @author Giulio Rossetti <Giulio.Rossetti@gmail.com>
 *   @version 1.0 31\05\2009
 *
 *   This file is part of "HotSpot Locator".
 *
 *   "HotSpot Locator" is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   "HotSpot Locator" is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package HotSpotLocator;

import com.eightmotions.map.*;
import com.eightmotions.util.UtilMidp;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.location.*;
import org.netbeans.microedition.lcdui.SplashScreen;

/**
 * @author Giulio Rossetti <Giulio.Rossetti@gmail.com>
 * @version 1.0 31\05\2009
 */
public class HotSpot extends MIDlet implements CommandListener {

    private static String CATEGORY = "Dati";
    private boolean midletPaused = false;
    private boolean[] flag;
    private SplashScreen Splash;
    private List Menu;
    private Form Carica, Cancella, Detail;
    private ChoiceGroup choiceGroup;
    private TextField nomeLocalita, descrizioneLocalita;
    private StringItem latitudineLocalita, longitudineLocalita;
    private Command Indietro, Visualizza, Salva, Elimina, Exit, SalvaDettagli;
    private Image splashscreen, elenco, cestino;
    private Ticker ticker,tickerMappa;
    private Alert alert;
    private Spacer spacer;
    private Canvas mappa;
    private MapDisplay m_map;
    private Display m_display;    
    private LandmarkStore ls;
    private Coordinates position;
    
   
    /**
     * Costruttore della classe HotSpot.
     * Inizializza il LandmarkStore e ottiene le coordinate attuali del device avviando
     * un thread apposito.
     */
    public HotSpot() {

        ls = LandmarkStore.getInstance(null);

        try {    
            if (ls.getCategories()==null){
                ls.addCategory(CATEGORY);
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        } catch (LandmarkException ex) {
            ex.printStackTrace();
        }

        PositionFetcher pf = new PositionFetcher();
        pf.run();
        this.position = pf.getPosition();        
    }

    //---------------------Metodi Midlet---------------------//
    
    /**
     * Inizializza l'applicazzione.
     */
    private void initialize() {
    }

    /**
     * Avvia la MIDlet.
     * Si preoccupa di caricare le UtilMidp necessarie a j2memap
     */
    public void startMIDlet() {
        UtilMidp.checkMIDP(this);
        switchDisplayable(null, getSplash());    
    }

    /**
     * Effettua un azione al resume della MIDlet
     */
    public void resumeMIDlet() {                                       
       
    }
    
    /**
     * Invocato all'avvio della MIDlet
     */
    public void startApp() {
        if (midletPaused) {
            resumeMIDlet ();
        } else {
            initialize ();
            startMIDlet ();
        }
        midletPaused = false;
    }

    /**
     * Invocato quando la MIDlet è in pausa
     */
    public void pauseApp() {
        midletPaused = true;
    }

    /**
     * Invocato al segnale di terminazione della MIDlet
     * @param unconditional se true, la MIDlet deve essere necessariamente terminata e le risorse rilasciate.
     */
    public void destroyApp(boolean unconditional) {
    }

    /**
     * Ritorna un instanza del display
     * @return l'istanza del display.
     */
    public Display getDisplay () {
        return Display.getDisplay(this);
    }

    /**
     * Termina la MIDlet.
     */
    public void exitMIDlet() {
        switchDisplayable (null, null);
        destroyApp(true);
        notifyDestroyed();
    }

    //-------------------------Layer-------------------------//

    /**
     * Ritorna lo splashscreen dell'applicazione
     * @return l'istanza inizializzata dello splashscreen
     */
    private SplashScreen getSplash() {
        if (Splash == null) {
            Splash = new SplashScreen(getDisplay());
            Splash.setTitle("Hot Spot Locator");
            Splash.setCommandListener(this);
            Splash.setFullScreenMode(true);
            Splash.setImage(getSplashScreen());
            Splash.setTimeout(2500);
        }
        return Splash;
    }

    /**
     * Ritorna il menù principale dell'applicazione
     * @todo gestire la ricerca per nome delle località
     *
     * @return l'istanza inizializzata del menù principale
     */
    private List getMenu() {
        if (Menu == null) {
            Menu = new List("Men\u00F9", Choice.IMPLICIT);
            Menu.append("Posizione Attuale (GPS)", getImmagineElenco());
            //Menu.append("Ricerca Località",getImmagineElenco());
            Menu.append("Carica Località Salvata", getImmagineElenco());
            Menu.append("Cancella Località",getImmagineElenco());
            Menu.append("Cancella Tutti i dati Salvati", getImmagineElenco());

            Menu.setTicker(getTicker());
            Menu.addCommand(getExit());
            Menu.setCommandListener(this);
            Menu.setFitPolicy(Choice.TEXT_WRAP_DEFAULT);
            Menu.setSelectedFlags(new boolean[] { false, false, false, false });
        }
        return Menu;
    }

    /**
     * Ritorna il menù di selezione per le località salvate
     * @return l'istanza inizializzata del menù di selezione
     */
    private Form getCarica() {        
        Carica = new Form("form", new Item[] { getChoiceGroup() });
        Carica.addCommand(getIndietro());
        Carica.addCommand(getVisualizza());
        Carica.setCommandListener(this);
        
        return Carica;
    }

    /**
     * Ritorna la mappa centrata nelle coordinate passate come parametro se presenti
     * @param coordinates coordinate attuali (GPS) oppure di località salvata
     * @return l'istanza inizializzata della mappa
     */
    private Canvas getMapCanvas(Coordinates coordinates){

            m_map=new MapDisplay();
            m_map.followUser();

            mappa = m_map.getCanvas();
            m_map.setCurMap(m_map.getMapByName("GoogleMap"));

            // Sto caricando una locazione salvata
            if(coordinates!=null){
                m_map.gotoLonLat(new Float(coordinates.getLongitude()), new Float(coordinates.getLatitude()),13);

            // Sto usando un dispositivo GPS
            } else if(coordinates==null && position!=null){
               if(position.getLatitude()!= 0.0 && position.getLongitude()!= 0.0){
                    m_map.gotoLonLat(new Float(position.getLatitude()), new Float(position.getLongitude()),13);

               // GPS non presente e/o fetch dei dati fallito
               } else {
                   m_map.gotoLonLat(new Float(0.0),new Float(0.0), 0);
               }

            // GPS non presente\errore nella localizzazione del dispositivo
            } else if (coordinates==null && position==null){
                m_map.gotoLonLat(new Float(0.0),new Float(0.0), 0);
            }

            // Ottengo le informazioni sugli HotSpot
            TrackFetcher tf = new TrackFetcher(m_display,m_map);
            tf.run();

            mappa.setFullScreenMode(false);
            mappa.setTitle("Hot Spot Locator");
            mappa.setTicker(getTickerMappa());
            mappa.addCommand(getIndietro());
            mappa.addCommand(getSalva());
            mappa.setCommandListener(this);

        return mappa;
    }

    /**
     * Ritorna la scheda di salvataggio località dettagliata
     * @return l'istanza inizializzata della scheda di salvataggio località dettagliata
     */
    private Form getDetail() {
        Detail = new Form("Dettagli Località",
                     new Item[] {getNomeLocalita(), getDescrizioneLocalita(),
                                 getSpacer(), getLatitudineLocalita(),
                                 getLongitudineLocalita()});

        Detail.addCommand(getSalvaDettagli());
        Detail.setCommandListener(this);
        
        return Detail;
    }

    /**
     * Ritorna la scheda selezione per la cancellazione di località
     * @return l'istanza inizializzata del menù di cancellazione località
     */
    private Form getCancellaLocalita(){
        Cancella = new Form("form", new Item[] { getChoiceGroup() });
        Cancella.addCommand(getIndietro());
        Cancella.addCommand(getElimina());
        Cancella.setCommandListener(this);        
        return Cancella;
    }
    
    //------------------Azioni Navigazione------------------//

    /**
     * Navigazione: Salva
     * @return comando di navigazione
     */
    private Command getSalva() {
        if (Salva == null) {
            Salva = new Command("Salva", Command.SCREEN, 0);
        }
        return Salva;
    }

    /**
     * Navigazione: Indietro
     * @return comando di navigazione
     */
    private Command getIndietro() {
        if (Indietro == null) {
            Indietro = new Command("Indietro", Command.BACK, 0);
        }
        return Indietro;
    }

   /**
     * Navigazione: Visualizza
     * @return comando di navigazione
     */
    private Command getVisualizza() {
        if (Visualizza == null) {
            Visualizza = new Command("Visualizza", Command.OK, 0);
        }
        return Visualizza;
    }

    /**
     * Navigazione: Elimina
     * @return comando di navigazione
     */
    private Command getElimina(){
        if (Elimina == null) {
            Elimina = new Command("Elimina", Command.OK, 0);
        }
        return Elimina;
    }

    /**
     * Navigazione: Exit
     * @return comando di navigazione
     */
    private Command getExit() {
        if (Exit == null) {
            Exit = new Command("Exit", Command.EXIT, 0);
        }
        return Exit;
    }

    /**
     * Navigazione: SalvaDettagli
     * @return comando di navigazione
     */
    private Command getSalvaDettagli() {
        if (SalvaDettagli == null) {
            SalvaDettagli = new Command("Salva", Command.SCREEN, 0);
        }
        return SalvaDettagli;
    }

    //---------------Gestione Layer e Comandi---------------//

    /**
     * Chiamato dal sistema quando viene invocato un comando da un oggetto di tipo displayable
     * @param command il comando invocato
     * @param displayable l'oggetto displayable che ha invocato il comando
     */
    public void commandAction(Command command, Displayable displayable) {
        if (displayable == Carica) {
            if (command == Indietro) {
                switchDisplayable(null, getMenu());
            } else if (command == Visualizza) {
                try {
                    Enumeration collection = (Enumeration) ls.getLandmarks(CATEGORY, choiceGroup.getString(choiceGroup.getSelectedIndex()));
                    Landmark location  = (Landmark) collection.nextElement();
                    switchDisplayable(null, getMapCanvas(location.getQualifiedCoordinates()));
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }

        } else if (displayable == Menu) {
            if (command == Exit) {
                exitMIDlet();
            } else if (command == List.SELECT_COMMAND) {
                MenuAction();
            }
        } else if (displayable == Splash) {
            if (command == SplashScreen.DISMISS_COMMAND) {
                switchDisplayable(null, getMenu());
            }
        } else if (displayable == mappa){
            if (command == Indietro){
                switchDisplayable(null, getMenu());
            }
            else if (command == Salva){
                switchDisplayable(null, getDetail());
            }
        } else if (displayable == Detail) {
            if (command == SalvaDettagli) {
                Landmark item = new Landmark(nomeLocalita.getString(),nomeLocalita.getString(),new QualifiedCoordinates(m_map.getLat(),m_map.getLon(),0,0,0),null);
                try {
                    ls.addLandmark(item, CATEGORY);
                } catch (IOException ex) {
                    ex.printStackTrace();
                }

               switchDisplayable(null, getMenu());
            }
        } else if (displayable == Cancella){           
             if (command == Indietro) {
                switchDisplayable(null, getMenu());
            } else if (command == Elimina) {
                DropLandmarks(choiceGroup.getString(choiceGroup.getSelectedIndex()));
                alert = new Alert("Rimozione Località",
                                  "Località rimossa con successo", null, AlertType.CONFIRMATION);
                alert.setTimeout(Alert.FOREVER);
                alert.setImage(getCestino());

                Display display = this.getDisplay();
                display.setCurrent(alert, Menu);
            }

        }
    }

    /**
     * Esegue le azioni associate all'elemento selezionato dalla lista del Menù
     */
    public void MenuAction() {
        String __selectedString = getMenu().getString(getMenu().getSelectedIndex());
        if (__selectedString != null) {
            if (__selectedString.equals("Posizione Attuale (GPS)")) {
                switchDisplayable(null, getMapCanvas(null));
            //} else if (__selectedString.equals("Ricerca Località")) {
            //    switchDisplayable(null, getCarica());
            } else if (__selectedString.equals("Carica Località Salvata")) {
                switchDisplayable(null, getCarica());
            } else if (__selectedString.equals("Cancella Località")) {
                switchDisplayable(null, getCancellaLocalita());
            } else if (__selectedString.equals("Cancella Tutti i dati Salvati")) {
                this.DropAllLandmarks();
                alert = new Alert("Rimozione Database Località",
                                  "Database rimosso con successo", null, AlertType.CONFIRMATION);
                alert.setTimeout(Alert.FOREVER);
                alert.setImage(getCestino());
                
                Display display = this.getDisplay();
                display.setCurrent(alert, Menu);
            }
        }
    }

    /**
     * Efettua lo switch per l'oggetto displayable corrente. Il <code>display</code> è ottenuto dal metodo <code>getDisplay</code>.
     * Questo metodo è usato da tutte le azioni che debbono effettuare operazioni di switch di oggetti displayable.
     * @param alert l'alert che deve essere temporaneamente mostrato; se <code>null</code>, allora <code>nextDisplayable</code> è impostato immediatamente.
     * @param nextDisplayable il displayable da impostare come visibile
     */
    private void switchDisplayable(Alert alert, Displayable nextDisplayable) {
        Display display = getDisplay();
        if (alert == null) {
            try{
                display.setCurrent(nextDisplayable);
            }catch(Exception e){
                e.printStackTrace();
            }
        } else {
            display.setCurrent(alert, nextDisplayable);
        }
    }
    
    //------------------Componenti Grafici------------------//
           
    /**
     * Ritorna l'immagine dello splashscreen
     * @return l'immagine caricata
     */
    public Image getSplashScreen() {
        if (splashscreen == null) {
            try {
                splashscreen = Image.createImage("/Images/splash.jpg");
            } catch (java.io.IOException e) {
                e.printStackTrace();
            }
        }
        return splashscreen;
     }

    /**
     * Ritorna l'immagine per gli item degli elenchi di selezione
     * @return l'immagine caricata
     */
    public Image getImmagineElenco() {
        if (elenco == null) {
            try {
                elenco = Image.createImage("/Images/puntoelenco.gif");
            } catch (java.io.IOException e) {
                e.printStackTrace();
            }
        }
        return elenco;
    }

     /**
     * Ritorna l'immagine per la cancellazione delle località
     * @return l'immagine caricata
     */
    public Image getCestino() {
        if (cestino == null) {
            try {
                cestino = Image.createImage("/Images/trash.jpg");
            } catch (java.io.IOException e) {
                e.printStackTrace();
            }
        }
        return cestino;
     }
      
    /**
     * Ritorna il ticker per il menu principale
     * @return il tiker inizializzato
     */
    public Ticker getTicker() {
        if (ticker == null) {
            ticker = new Ticker("Visualizza gli Hotspot FON vicini a te oppure quelli di una zona precedentemente salvata...");
        }
        return ticker;
    }

   /**
     * Ritorna il ticker per il la mappa
     * @return il tiker inizializzato
     */
    public Ticker getTickerMappa() {
        if (tickerMappa == null) {
            tickerMappa = new Ticker("1/ZoomOut - 3/ZoomIn - 5/SelezioneMappa - 7/NavigazioneAP - 9/ZoomAP");
        }
        return tickerMappa;
    }

    /**
     * Ritorna il textfield per il nome della località
     * @return textfield per il nome della località
     */
    public TextField getNomeLocalita() {
        nomeLocalita = new TextField("Nome", null, 32, TextField.ANY);
        return nomeLocalita;
    }

   /**
     * Ritorna il textfield per la descrizione della località
     * @return textfield per la descrizione della località
     */
    public TextField getDescrizioneLocalita() {
        descrizioneLocalita = new TextField("Descrizione", null, 32, TextField.ANY);
        return descrizioneLocalita;
    }

    /**
     * Ritorna le informazioni sulla latitudine della località
     * @return informazioni sulla latitudine della località
     */
    public StringItem getLatitudineLocalita() {
        latitudineLocalita = new StringItem("Latitudine", null);
        latitudineLocalita.setText(""+m_map.getLat());
        return latitudineLocalita;
    }

    /**
     * Ritorna le informazioni sulla longitudinte della località
     * @return informazioni sulla longitudine della località
     */
    public StringItem getLongitudineLocalita() {
        longitudineLocalita = new StringItem("Longitudine", null);
        longitudineLocalita.setText(""+m_map.getLon());
        return longitudineLocalita;
    }
  
    /**
     * Ritorna l'elenco inizializzato delle località salvate
     * @return elenco delle località salvate
     */
    public ChoiceGroup getChoiceGroup() {        
        Vector items = new Vector();
        choiceGroup = new ChoiceGroup("Localit\u00E0", Choice.EXCLUSIVE);
        try {
            Enumeration landmarks = ls.getLandmarks();
            if(landmarks != null && landmarks.hasMoreElements()) {
                while(landmarks.hasMoreElements()) {
                    Landmark land = (Landmark)landmarks.nextElement();
                    items.addElement(land);
                }

            } else {
                System.out.println("There is no landmark in the landmark store");
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }

        if (items.size()>0){
            flag = new boolean[items.size()];
            for(int i=0; i<items.size(); i++){
                choiceGroup.append( ((Landmark)items.elementAt(i)).getName(), null);
                flag[i] = false;
            }
        } else {
            flag = new boolean[0];
        }

        choiceGroup.setSelectedFlags(flag);
        choiceGroup.setFont(0, null);

        return choiceGroup;
    }

     /**
     * Ristorna uno spacer
     * @return uno spacer
     */
    public Spacer getSpacer() {
        if (spacer == null) {
            // write pre-init user code here
            spacer = new Spacer(16, 1);
            // write post-init user code here
        }
        return spacer;
    }

    //----------------------Utilità-----------------------//
  
    /**
     * Ottiene la mappa con il posizionamento attuale ricavato tramite GPS o cella.
     * @throws LocationException
     */
    public void refreshLocalPosition() throws LocationException{
        PositionFetcher pf = new PositionFetcher();
        pf.run();
        this.position = pf.getPosition();
    }

    /**
     * Cancella tutte le località salvate
     */
    private void DropAllLandmarks(){
        Enumeration enum1;
        try {
            enum1 = ls.getLandmarks();
            while (enum1.hasMoreElements()){
                Landmark lm1 =(Landmark) enum1.nextElement();
                ls.deleteLandmark(lm1);
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }catch (LandmarkException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Cancella la località specificata in input
     * @param nome il nome della località da cancellare
     */
    private void DropLandmarks(String nome){
        try {
            Enumeration l =  ls.getLandmarks(CATEGORY, nome);
            ls.deleteLandmark((Landmark) l.nextElement());
            } catch (IOException ex) {
                ex.printStackTrace();
            } catch (LandmarkException ex) {
                ex.printStackTrace();
            }
    }

}
